package com.nineclock.sign.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nineclock.sign.mapper.SignMapper;
import com.nineclock.sign.mapper.SignPictureMapper;
import com.nineclock.sign.pojo.SignPicture;
import com.alibaba.nacos.common.utils.StringUtils;
import com.nineclock.sign.dto.SignAllUserDTO;
import com.nineclock.sign.mapper.SignInfoMapper;
import com.nineclock.sign.service.SignService;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.threadlocal.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.sign.dto.SignDTO;
import com.nineclock.sign.pojo.SignInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nineclock.common.entity.PageResult;
import com.nineclock.sign.dto.SignInfoDTO;
import com.nineclock.sign.dto.SignPCListDTO;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.dto.SysDepartmentDTO;
import com.nineclock.system.feign.SysUserFeign;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.HorizontalAlignment;

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;


import java.util.List;

import java.util.stream.Collectors;

@Slf4j
@Service
public class SignServiceImpl implements SignService {

    @Autowired
    private SignMapper signMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private SignInfoMapper signInfoMapper;
    @Autowired
    private SignPictureMapper signPictureMapper;
    @Autowired
    private SysUserFeign sysUserFeign;


    /**
     * 实现签到
     *
     * @param signDTO
     */
    @Override
    public void sign(SignDTO signDTO) {
        if (signDTO == null) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }


        //转换pojo
        SignInfo signInfo = BeanHelper.copyProperties(signDTO, SignInfo.class);
        //设置基础字段
        signInfo.setCompanyId(CurrentUserHolder.get().getCompanyId());
        signInfo.setCompanyUserId(CurrentUserHolder.get().getCompanyUserId());
        signInfo.setCreateTime(new Date());
        signInfo.setCreateDate(DateTimeUtil.dateToStr(new Date(), "yyyy-MM-dd"));
        signInfo.setCreateMonth(DateTimeUtil.dateToStr(new Date(), "yyyy-MM"));
        signMapper.insert(signInfo);
        SignPicture signPicture = new SignPicture();
        String images = signDTO.getImages();
        signPicture.setUrl(images);
        signPicture.setSignId(signInfo.getId());
        signPictureMapper.insert(signPicture);
        //记录或更新用户当日打卡记录
        String date = signInfo.getCreateDate();
        Long companyUserId = signInfo.getCompanyUserId();
        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyUserId, companyUserId);
        wrapper.eq(SignInfo::getCreateDate, date);
        Integer count = signMapper.selectCount(wrapper);
        String key = signInfo.getCompanyUserId() + signInfo.getCreateDate();
        if (count != null) {
            redisTemplate.boundValueOps(key).set(count + "");
        }


        //每月打卡记录
        String createMonth = signInfo.getCreateMonth();
        Long companyUserId2 = signInfo.getCompanyUserId();
        LambdaQueryWrapper<SignInfo> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(SignInfo::getCreateMonth, createMonth);
        wrapper1.eq(SignInfo::getCompanyUserId, companyUserId2);
        Integer count1 = signMapper.selectCount(wrapper1);
        String key1 = signInfo.getCompanyUserId() + signInfo.getCreateMonth();
        if (count1 != null) {
            redisTemplate.boundValueOps(key1).set(count1 + "");
        }
    }

    @Override
    public Integer getDayCount() {
        //获取当前用户id,当前企业id
        Long companyId = CurrentUserHolder.get().getCompanyId();
        //查询当前登录用户
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();
        //当天日期
        String dayStr = DateTimeUtil.dateToStr(new Date(), "yyyy-MM-dd");

        return this.dayCount(companyId, companyUserId, dayStr);
    }

    @Override
    public List<SignDTO> pagingSignCurrentUserByMonth(String dateStr) {
        //健壮性判断
        if (StrUtil.isEmpty(dateStr)) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }

        log.info("参数为:::" + dateStr);
        //获取当前用户id,当前企业id
        Long companyId = CurrentUserHolder.get().getCompanyId();
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();

        //构造查询器
        LambdaQueryWrapper<SignInfo> signInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        signInfoLambdaQueryWrapper.eq(SignInfo::getCompanyId, companyId)
                .eq(SignInfo::getCompanyUserId, companyUserId)
                .eq(SignInfo::getCreateMonth, dateStr);

        List<SignInfo> signInfoList = signInfoMapper.selectList(signInfoLambdaQueryWrapper);
        //创建空结果集合
        List<SignDTO> signDTOList = new ArrayList<>();
        //健壮性判断
        if (CollectionUtil.isEmpty(signInfoList)) {
            throw new NcException(ResponseEnum.SIGN_DATA_NULL);
        }

        Integer monthCount = this.monthCount(companyId, companyUserId, dateStr);
        Integer dayCount = this.getDayCount();
        for (SignInfo signInfo : signInfoList) {
            //赋值
            SignDTO signDTO = BeanHelper.copyProperties(signInfo, SignDTO.class);
            signDTO.setDayCount(String.valueOf(dayCount));
            signDTO.setMonthCount(String.valueOf(monthCount));
            //加入结果集集合
            signDTOList.add(signDTO);
        }
        return this.queryImage(signDTOList);
    }

    @Override
    public List<SignAllUserDTO> listSignAllUser(String dateStr) {
        //健壮性判断
        if (StrUtil.isEmpty(dateStr)) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        //获取当前用户id,当前企业id
        Long companyId = CurrentUserHolder.get().getCompanyId();
        //查询当前登录用户
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();
        //创建空结果集
        List<SignAllUserDTO> signAllUserDTOList = new ArrayList<>();
        //查询当前企业所有员工
        List<SysCompanyUserDTO> sysCompanyUserDTOList = sysUserFeign.queryAllCompanyUser().getData();

        //处理数据
        for (SysCompanyUserDTO sysCompanyUserDTO : sysCompanyUserDTOList) {
            //创建一条新的数据
            SignAllUserDTO signAllUserDTO = new SignAllUserDTO();
            //赋值
            signAllUserDTO.setUsername(sysCompanyUserDTO.getUserName());
            //查询现在遍历的这个用户的打卡记录
            LambdaQueryWrapper<SignInfo> signInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
            signInfoLambdaQueryWrapper.eq(SignInfo::getCompanyId, companyId)
                    .eq(SignInfo::getCompanyUserId, sysCompanyUserDTO.getId())
                    .eq(SignInfo::getCreateDate, dateStr);

            List<SignInfo> signInfoList = signInfoMapper.selectList(signInfoLambdaQueryWrapper);

            //非空判断
            if (CollectionUtil.isNotEmpty(signInfoList)) {
                List<SignDTO> signDTOList = BeanHelper.copyWithCollection(signInfoList, SignDTO.class);
                //设置没赋值的字段
                for (SignDTO signDTO : signDTOList) {
                    signDTO.setUserName(sysCompanyUserDTO.getUserName());
                    signDTO.setDayCount(String.valueOf(this.dayCount(companyId, signDTO.getCompanyUserId(), dateStr)));
                    signDTO.setMonthCount(String.valueOf(this.monthCount(companyId, signDTO.getCompanyUserId(), DateTimeUtil.dateToStr(DateTimeUtil.strToDate(dateStr, "yyyy-MM-dd"), "yyyy-MM"))));
                }
                signDTOList = this.queryImage(signDTOList);
                signAllUserDTO.setSignResponseList(signDTOList);
                signAllUserDTOList.add(signAllUserDTO);
            }
        }
        return signAllUserDTOList;
    }

    public Integer dayCount(Long companyId, Long companyUserId, String dateStr) {
        //构造查询器
        LambdaQueryWrapper<SignInfo> signInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        signInfoLambdaQueryWrapper.eq(SignInfo::getCompanyId, companyId)
                .eq(SignInfo::getCompanyUserId, companyUserId)
                .eq(SignInfo::getCreateDate, dateStr);

        Integer dayCount = signInfoMapper.selectCount(signInfoLambdaQueryWrapper);
        return dayCount;
    }

    //统计某月某员工打卡次数
    public Integer monthCount(Long companyId, Long companyUserId, String dateStr) {
        //构造查询器
        LambdaQueryWrapper<SignInfo> signInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        signInfoLambdaQueryWrapper.eq(SignInfo::getCompanyId, companyId)
                .eq(SignInfo::getCompanyUserId, companyUserId)
                .eq(SignInfo::getCreateMonth, dateStr);

        Integer monthCount = signInfoMapper.selectCount(signInfoLambdaQueryWrapper);
        return monthCount;
    }

    //根据签到列表查询签到图片
    public List<SignDTO> queryImage(List<SignDTO> signDTOList) {
        //健壮性判断
        if (CollectionUtil.isEmpty(signDTOList)) {
            return signDTOList;
        }

        for (SignDTO signDTO : signDTOList) {
            LambdaQueryWrapper<SignPicture> signPictureLambdaQueryWrapper = new LambdaQueryWrapper<>();
            signPictureLambdaQueryWrapper.eq(SignPicture::getSignId, signDTO.getId());
            List<SignPicture> signPictureList = signPictureMapper.selectList(signPictureLambdaQueryWrapper);
            if (CollectionUtil.isNotEmpty(signPictureList)) {
                List<String> imageUrlList = signPictureList.stream().map(signPicture -> {
                    return signPicture.getUrl();
                }).collect(Collectors.toList());
                String images = StringUtils.join(imageUrlList, ",");
                signDTO.setImages(images);
                signDTO.setSignPictureUrlList(imageUrlList);
            }
        }
        return signDTOList;
    }

    @Override
    public PageResult<SignPCListDTO> querySignPCListPage(Integer page, Integer pageSize, String startTime, String endTime) {
        //校验参数
        if (page == null || pageSize == null || StrUtil.isEmpty(startTime) || StrUtil.isEmpty(endTime)) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        // 获取指定时间范围内的时间列表
        List<Date> dateList = DateTimeUtil.getDatesBetweenTwoDate(startTime, endTime, "yyyy-MM-dd");
        List<String> dateStrList = dateList.stream().map(date -> {
            return DateTimeUtil.dateToStr(date, "yyyy-MM-dd");
        }).collect(Collectors.toList());


        Page<SignInfo> signInfoPage = new Page<>(page, pageSize);


        //查询条件
        //当前企业下的  创建时间在 开始 和 结束 之间的  信息
        LambdaQueryWrapper<SignInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());
        queryWrapper.between(SignInfo::getCreateDate, startTime, endTime);
        //todo mysql5.7 group by 问题

        signInfoPage = signInfoMapper.selectPage(signInfoPage, queryWrapper);

        //获取  row
        List<SignInfo> signInfos = signInfoPage.getRecords();

        if (CollectionUtil.isEmpty(signInfos)) {
            return new PageResult<>();
        }


        List<SysCompanyUserDTO> sysCompanyUserDTOS = signInfos.stream().map(signInfo -> {
            return sysUserFeign.queryByCompanyUserId(signInfo.getCompanyUserId()).getData();
        }).collect(Collectors.toList());


        List<SignPCListDTO> collect = new ArrayList<>();
        if (sysCompanyUserDTOS != null) {
            for (SysCompanyUserDTO sysCompanyUserDTO : sysCompanyUserDTOS) {
                SignPCListDTO signPCListDTO = new SignPCListDTO();
                signPCListDTO.setUsername(sysCompanyUserDTO.getUserName());
                signPCListDTO.setWorkNumber(sysCompanyUserDTO.getWorkNumber());
                signPCListDTO.setPost(sysCompanyUserDTO.getPost());


                SysDepartmentDTO sysDepartmentDTO = new SysDepartmentDTO();
                sysDepartmentDTO.setId(sysCompanyUserDTO.getId());
                sysDepartmentDTO.setName(sysCompanyUserDTO.getDepartmentName());
                signPCListDTO.setDepartment(sysDepartmentDTO);
                signPCListDTO.setSigns(
                        getSigns(dateStrList, sysCompanyUserDTO.getId()));

                collect.add(signPCListDTO);
            }
        }


        //组装结果并返回
        return new PageResult<>(signInfoPage.getTotal(), signInfoPage.getPages(), collect);
    }


    /**
     * 导出指定时间段内的考勤数据
     *
     * @param startTime
     * @param endTime
     */
    @Override
    public void exportSignData(String startTime, String endTime) throws IOException {
        // 获取考勤数据,默认查询1000条
        List<SignPCListDTO> rows = querySignPCListPage(1, 1000, startTime, endTime).getRows();

        //
        //2. 获取响应对象，并设置 响应对象
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletResponse response = servletRequestAttributes.getResponse();
        response.setHeader("Content-disposition", "attachment;filename=Attendance.xlsx");
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding("utf-8");

        // *************easyExcel **********

        WriteCellStyle cellStyle = new WriteCellStyle();
        //表头和内容都是水平居中
        cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        //设置字体
        WriteFont font = new WriteFont();
        font.setFontName("宋体");
        font.setFontHeightInPoints((short) 12);
        cellStyle.setWriteFont(font);

        HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(cellStyle, cellStyle);

        EasyExcel.write(response.getOutputStream())
                .head(handlerHead(rows))
                .registerWriteHandler(styleStrategy)
                .registerWriteHandler(new MyColumnStyle())
                .sheet("测试数据")
                .doWrite(handlerData(rows));


    }

    /**
     * 处理签到列表数据信息
     *
     * @param rows
     * @return
     */
    private List<List<String>> handlerData(List<SignPCListDTO> rows) {
        List<List<String>> data = new ArrayList<>();
        //循环固定字段
        for (SignPCListDTO row : rows) {
            List<String> list = new ArrayList<>();
            list.add(row.getUsername());
            list.add(row.getWorkNumber());
            list.add(row.getDepartment().getName());
            list.add(row.getPost());
            //遍历非固定字段
            List<SignInfoDTO> signs = row.getSigns();
            for (SignInfoDTO sign : signs) {
                list.add(sign.getSignCount().toString());
            }
            data.add(list);
        }


        return data;
    }

    /**
     * 处理签到列表表头信息
     *
     * @param rows
     * @return
     */
    private List<List<String>> handlerHead(List<SignPCListDTO> rows) {
        List<List<String>> head = new ArrayList<>();
        head.add(Arrays.asList("姓名"));
        head.add(Arrays.asList("工号"));
        head.add(Arrays.asList("部门"));
        head.add(Arrays.asList("职位"));
        SignPCListDTO signPCListDTO = rows.get(0);
        List<SignInfoDTO> signs = signPCListDTO.getSigns();
        for (SignInfoDTO sign : signs) {
            head.add(Arrays.asList(sign.getSignDate()));
        }
        return head;
    }


    /**
     * 封装数据到SignInfoDto中
     *
     * @param dateStrList
     * @param companyId
     * @return
     */
    private List<SignInfoDTO> getSigns(List<String> dateStrList, Long companyId) {
        List<SignInfoDTO> signInfoDTOS = new ArrayList<>();
        for (String dateStr : dateStrList) {
            SignInfoDTO signInfoDTO = new SignInfoDTO();
            signInfoDTO.setSignDate(dateStr);

            LambdaQueryWrapper<SignInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(SignInfo::getCreateDate, dateStr);
            lambdaQueryWrapper.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());
            lambdaQueryWrapper.in(SignInfo::getCompanyUserId, companyId);
            Integer count = signInfoMapper.selectCount(lambdaQueryWrapper);
            signInfoDTO.setSignCount(count);
            signInfoDTOS.add(signInfoDTO);
        }
        //结果返回
        return signInfoDTOS;
    }

    class MyColumnStyle extends AbstractColumnWidthStyleStrategy {

        @Override
        protected void setColumnWidth(WriteSheetHolder writeSheetHolder,
                                      List<CellData> cellDataList,
                                      Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 4500);
        }
    }
}
